﻿using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Xml.Linq;
using Maple.Weixin.MP.Entities;
using Maple.Weixin.MP.Exceptions;
using Maple.Weixin.MP.OAuth;

namespace Maple.Weixin.MP.Helpers
{
    public static class WeixinHelper
    {
        private const string AccessTokenUrl =
            "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}";

        private const string UploadMediaUrl =
            "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token={0}&type={1}";

        private const string DownloadMediaUrl =
            "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token={0}&media_id={1}";

        private const string TemplateMessageUrl =
            "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}";

        private const string MenuCreateUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token={0}";

        private const string OAuthUrl =
            "https://open.weixin.qq.com/connect/oauth2/authorize?appid={0}&redirect_uri={1}&response_type={2}&scope={3}&state={4}#wechat_redirect";

        private const string OAuthAccessTokenUrl =
            "https://api.weixin.qq.com/sns/oauth2/access_token?appid={0}&secret={1}&code={2}&grant_type={3}";

        private const string OAuthRefreshTokenUrl =
            "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid={0}&grant_type={1}&refresh_token={2}";

        private const string OAuthUserInfoUrl =
            "https://api.weixin.qq.com/cgi-bin/user/info?access_token={0}&openid={1}&lang={2}";

        private const string QRCodeTicketUrl = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={0}";

        private const string OAuthAccessTokenVerifyUrl =
            "https://api.weixin.qq.com/sns/auth?access_token={0}&openid={1}";

        private const string QRCodeUrl = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={0}";

        private const string DkfOnlineListUrl =
            "https://api.weixin.qq.com/cgi-bin/customservice/getonlinekflist?access_token={0}";

        private const string DkfListUrl =
            "https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token={0}";

        private const string DkfAccountUrl =
            "https://api.weixin.qq.com/cgi-bin/customservice/getkflist?access_token={0}&kf_account={1}&kf_nick={2}&kf_id={3}";

        private const string DkfAddUrl = "https://api.weixin.qq.com/customservice/kfaccount/add?access_token={0}";

        private const string DkfUpdateUrl = "https://api.weixin.qq.com/customservice/kfaccount/update?access_token={0}";

        private const string DkfDelUrl =
            "https://api.weixin.qq.com/customservice/kfaccount/del?access_token={0}&kf_account={1}";

        private const string DkfGetRecord =
            "https://api.weixin.qq.com/cgi-bin/customservice/getrecord?access_token={0}&openid={1}&starttime={2}&endtime={3}&pagesize={4}&pageindex={5}";

        private static DateTime baseTime = new DateTime(1970, 1, 1); //Unix起始时间

        public static string GetDkfAddUrl(string accessToken) {
            return String.Format(DkfAddUrl, accessToken);
        }

        public static string GetDkfUpdateUrl(string accessToken) {
            return String.Format(DkfUpdateUrl, accessToken);
        }

        public static string GetDkfDelUrl(string accessToken, string kfAccount) {
            return String.Format(DkfDelUrl, accessToken, kfAccount);
        }

        public static string GetDkfListUrl(string accessToken) {
            return String.Format(DkfListUrl, accessToken);
        }

        public static string GetDkfAccountUrl(string accessToken ,string accountName,string nickName,string id)
        {
            return String.Format(DkfAccountUrl,accessToken, accountName, nickName, id);
        }

        public static string GetDkfOnlineListUrl(string accessToken) {
            return String.Format(DkfOnlineListUrl, accessToken);
        }

        /// <summary>
        ///  获取客服聊天记录url
        /// </summary>
        /// <param name="accessToken"></param>
        /// <param name="openid"></param>
        /// <param name="starttime"></param>
        /// <param name="endtime"></param>
        /// <param name="pagesize"></param>
        /// <param name="pageindex"></param>
        /// <returns></returns>
        public static string GetDkfRecordsUrl(string accessToken, string openid, string starttime, string endtime, int pagesize, int pageindex)
        {
            return String.Format(DkfGetRecord, accessToken, openid, starttime, endtime, pagesize, pageindex);
        }

        public static string GetQRCode(string ticket) {
            return String.Format(QRCodeUrl, ticket);
        }

        public static string GetQRCodeTicketUrl(string accessToken) {
            return String.Format(QRCodeTicketUrl, accessToken);
        }

        public static string GetOAuthAccessTokenVerifyUrl(string accessToken, string openid) {
            return String.Format(OAuthAccessTokenVerifyUrl, accessToken, openid);
        }

        public static string GetOAuthUserInfoUrl(string accessToken, string openid, string lang = "zh-CN") {
            return String.Format(OAuthUserInfoUrl, accessToken, openid, lang);
        }

        public static string GetOAuthRefreshTokenUrl(string appid, string refreshToken,
            string grantType = "refresh_token") {
            return String.Format(OAuthRefreshTokenUrl, appid, grantType, refreshToken);
        }

        public static string GetOAuthAccessTokenUrl(string appid, string secret, string code,
            string grantType = "authorization_code") {
            return String.Format(OAuthAccessTokenUrl, appid, secret, code, grantType);
        }

        public static string GetAccessUrl(string appid, string appsecret) {
            return String.Format(AccessTokenUrl, appid, appsecret);
        }

        public static string GetUploadMediaUrl(string accessToken, UploadMediaType mediaType) {
            return String.Format(UploadMediaUrl, accessToken, mediaType.GetName());
        }

        public static string GetDownloadMediaUrl(string accessToken, string mediaid) {
            return String.Format(DownloadMediaUrl, accessToken, mediaid);
        }

        public static string GetTemplateMessageUrl(string accessToken) {
            return String.Format(TemplateMessageUrl, accessToken);
        }

        public static string GetMenuCreateUrl(string accessToken) {
            return String.Format(MenuCreateUrl, accessToken);
        }

        public static string GetOAuthUrl(string appid, string redirectUrl, string state, OAuthScope scope,
            string responseType = "code") {
            return String.Format(OAuthUrl, appid, HttpUtility.UrlEncode(redirectUrl), responseType, scope, state);
        }

        public static string GetSignature(string timestamp, string nonce, string token) {
            var array = new[] {token, timestamp, nonce}.OrderBy(c => c).ToArray();
            var strArray = String.Join("", array);
            var sha1 = SHA1.Create();
            var sha1Array = sha1.ComputeHash(Encoding.UTF8.GetBytes(strArray));
            var builder = new StringBuilder();
            foreach (var item in sha1Array) {
                builder.AppendFormat("{0:x2}", item);
            }

            return builder.ToString();
        }

        /// <summary>
        /// 从Xml文档中读取指定元素的值。
        /// </summary>
        /// <param name="document">Xml文档。</param>
        /// <param name="elementName">指定的元素名称。</param>
        /// <returns>指定元素的值。如果指定的元素不存在，则为null。</returns>
        public static string GetElementValue(this XDocument document, string elementName) {
            return GetElementValue(document, elementName, false);
        }

        /// <summary>
        /// 从Xml文档中读取指定元素的值。
        /// </summary>
        /// <param name="document">Xml文档。</param>
        /// <param name="elementName">指定的元素名称。</param>
        /// <param name="exceptionIfNotFound">如果指定的元素未找到，是否抛出异常。</param>
        /// <returns>指定元素的值。如果exceptionIfNotFound设置为true，则会抛出<seealso cref="BadXmlFormatException"/>异常。否则返回null。</returns>
        public static string GetElementValue(this XDocument document, string elementName, bool exceptionIfNotFound) {
            if (document.Root == null)
                throw new BadXmlFormatException(document);

            var element = document.Root.Element(elementName);
            if (element == null)
                if (exceptionIfNotFound)
                    throw new BadXmlFormatException(document);
                else
                    return null;

            return element.Value;
        }

        /// <summary>
        /// 从Xml文档中读取时间戳，并将其转换为.net的时间。
        /// </summary>
        /// <param name="document">Xml文档。</param>
        /// <param name="timestampName">时间戳的元素名称。</param>
        /// <returns>转换之后的.net时间。</returns>
        public static DateTime GetTimeStampValue(this XDocument document, string timestampName) {
            var text = GetElementValue(document, timestampName, true);
            var timestamp = long.Parse(text);
            return baseTime.AddTicks((timestamp + 8*60*60)*10000000);
        }

        public static long GetWeixinDateTime(DateTime dateTime) {
            return (dateTime.Ticks - baseTime.Ticks)/10000000 - 8*60*60;
        }
    }
}